package com.net.xpay.operator.secutiry;

import com.google.common.base.Strings;
import com.net.common.exception.BusinessException;
import com.net.common.util.SerializationUtil;
import com.net.xpay.common.domain.CheckCode;
import com.net.xpay.common.domain.operator.OperatorUser;
import com.net.xpay.common.enums.user.CheckCodeType;
import com.net.xpay.common.enums.user.LoginMethod;
import com.net.xpay.common.helper.RedisHelper;
import com.net.xpay.common.manager.CheckCodeManager;
import com.net.xpay.common.manager.operator.OperatorUserManager;
import com.net.xpay.common.validator.CheckCodeValidator;
import com.net.xpay.core.constant.PoseidonErrorCode;
import com.net.xpay.core.constant.RedisKey;
import lombok.extern.slf4j.Slf4j;
import org.mindrot.jbcrypt.BCrypt;
import org.springframework.beans.factory.annotation.Autowired;
import org.springframework.security.authentication.AuthenticationProvider;
import org.springframework.security.core.Authentication;
import org.springframework.security.core.AuthenticationException;
import org.springframework.stereotype.Component;


/**
 * @author  on 01/02/2018.
 */
@Component
@Slf4j
public class FormLoginAuthenticationProvider implements AuthenticationProvider {

    @Autowired
    private OperatorUserManager operatorUserManager;

    @Autowired
    private CheckCodeManager checkCodeManager;

    @Autowired
    private CheckCodeValidator checkCodeValidator;

    @Autowired
    private RedisHelper redisHelper;

    @Override
    public Authentication authenticate(Authentication authentication) throws AuthenticationException {
        FormLoginToken token = (FormLoginToken) authentication;
        if (Strings.isNullOrEmpty(token.getMobile())) {
            throw new WebAuthenticationException(PoseidonErrorCode.MOBILE_ERROR, "手机号格式不正确");
        } else if (token.getClientFrom() == null) {
            throw new WebAuthenticationException(PoseidonErrorCode.BUSINESS_ERROR, " 登陆来源错误");
        }

        try {
            return doAuthentication(token);
        } catch (AuthenticationException e) {
            throw e;
        } catch (Exception e) {
            log.warn("operatorUser authenticated failed|mobile={}|password={}|checkCode={}",
                    token.getMobile(), token.getPassword(), token.getCheckCode(), e);
            throw new WebAuthenticationException(PoseidonErrorCode.BUSINESS_ERROR, "登陆服务暂时不可用,请稍后重试");
        }
    }

    @Override
    public boolean supports(Class<?> authentication) {
        return FormLoginToken.class.isAssignableFrom(authentication);
    }

    private Authentication doAuthentication(FormLoginToken token) throws AuthenticationException {
        OperatorUser operatorUser = operatorUserManager.findByMobile(token.getMobile());
        if (operatorUser == null && token.getLoginMethod() != LoginMethod.CHECK_CODE) {
            throw new WebAuthenticationException(PoseidonErrorCode.MOBILE_NOT_FOUND, "手机号没找到");
        }

        verifyCredential(token, operatorUser);

        if (operatorUser == null) {
            throw new WebAuthenticationException(PoseidonErrorCode.MOBILE_NOT_FOUND, "手机号没找到");
        }

        return new Operator(operatorUser, token);
    }

    private void verifyCredential(FormLoginToken token, OperatorUser operatorUser) throws AuthenticationException {
        switch (token.getLoginMethod()) {
            case PASSWORD:
                verifyPassword(token, operatorUser);
                break;
            case CHECK_CODE:
                verifyCheckCode(token);
                break;
            default:
                throw new WebAuthenticationException(PoseidonErrorCode.BUSINESS_ERROR, "登陆方式不正确,请稍后重试");
        }
    }

    private void verifyPassword(FormLoginToken token, OperatorUser operatorUser) throws AuthenticationException {
        //验证密码
        String password = token.getPassword() == null ? "" : token.getPassword();
        if (!BCrypt.checkpw(password, operatorUser.getPassword())) {
            throw new WebAuthenticationException(PoseidonErrorCode.PASSWORD_ERROR, "密码不正确");
        }
        //验证图形验证码
        verifyImageCode(token);
    }

    private void verifyCheckCode(FormLoginToken token) throws AuthenticationException {
        try {
            CheckCode checkCode = checkCodeManager.findByMobileAndType(token.getMobile(), CheckCodeType.OPERATOR_USER_LOGIN);

            checkCodeValidator.validateCheckCode(checkCode, token.getCheckCode());
            //验证图形验证码
            verifyImageCode(token);
        } catch (BusinessException e) {
            log.warn("verifyCredential verifyCheckCode psw wrong token={}|checkCode={}", SerializationUtil.obj2String(token), token.getCheckCode());
            throw new WebAuthenticationException(PoseidonErrorCode.CHECK_CODE_ERROR, e.getErrorMessage());
        }
    }

    private void verifyImageCode(FormLoginToken token) throws AuthenticationException {
        //验证图形验证码
        try {
            // 在实际开发中，在redis中获取验证码
            String imageCode = null;
            Object statCache = redisHelper.get(RedisKey.IMAGE_CODE + token.getCodeKey());
            if (statCache != null) {
                imageCode = statCache.toString();
            }else {
                throw new WebAuthenticationException(PoseidonErrorCode.PASSWORD_ERROR, "图形验证码已过期");
            }

            // codeKey 作为redis的key
            if (Strings.isNullOrEmpty(imageCode)){
                throw new BusinessException(PoseidonErrorCode.UNKNOWN_ERROR, "系统异常");
            }

            if(!imageCode.equalsIgnoreCase(token.getImageCode())){
                throw new WebAuthenticationException(PoseidonErrorCode.PASSWORD_ERROR, "图形验证码不正确");
            }

        } catch (Exception e) {
            log.error("图形验证码校验失败", e);
            throw new WebAuthenticationException(PoseidonErrorCode.PASSWORD_ERROR, "图形验证码校验失败");
        }
    }
}